#include "precompiled.hpp"

#include "native.hpp"
#include "exec_context.hpp"

static void unknown_rule(jam_frame *frame, const char* key, 
	const jam_string &module_name, const jam_string &rule_name )
{
	frame->context_.backtrace_line(frame->prev_);
	frame->context_.err()<<key<<" error: rule \""<<
		rule_name<<"\" unknown in module "<<module_name<<std::endl;	
	frame->context_.backtrace(frame->prev_);
	throw jam_exception("Interpreter exception");
}

///////////////////////////////////////////////////////////////////////////////////////
////// ECHO rule - ECHO ( text )
///////////////////////////////////////////////////////////////////////////////////////
void echo_invoke(jam_frame *frame,jam_list *res)
{
	if (!frame->args_.empty())
	{
		const jam_list &text=frame->args_.front();
		std::ostream &out=frame->context_.out();

		out<<text;
		out<<std::endl;

		if (res) res->append_list(text);
	}
}
native_rule_entry echo_reg(JAM_ROOT_MODULE_NAME,"ECHO",&echo_invoke,
	jam_rule::vararg,"text");


///////////////////////////////////////////////////////////////////////////////////////
////// Swallow rule - SWALLOW ( args * )
///////////////////////////////////////////////////////////////////////////////////////
void swallow_invoke(jam_frame *frame,jam_list *res)
{
	if (!frame->args_.empty())
	{
		const jam_list &text=frame->args_.front();
		if (res) res->append_list(text);
	}
}
native_rule_entry swallow_reg(JAM_ROOT_MODULE_NAME,"SWALLOW",&swallow_invoke,
	jam_rule::vararg,"text");


///////////////////////////////////////////////////////////////////////////////////////
////// IMPORT RULE - IMPORT ( SOURCE_MODULE ? : SOURCE_RULES * : TARGET_MODULE ? : 
//////                        TARGET_RULES * : LOCALIZE ? )
///////////////////////////////////////////////////////////////////////////////////////
/*
* The IMPORT rule imports rules from the SOURCE_MODULE into the
* TARGET_MODULE as local rules. If either SOURCE_MODULE or
* TARGET_MODULE is not supplied, it refers to the global
* module. SOURCE_RULES specifies which rules from the SOURCE_MODULE
* to import; TARGET_RULES specifies the names to give those rules in
* TARGET_MODULE. If SOURCE_RULES contains a name which doesn't
* correspond to a rule in SOURCE_MODULE, or if it contains a
* different number of items than TARGET_RULES, an error is issued.
* if LOCALIZE is specified, the rules will be executed in
* TARGET_MODULE, with corresponding access to its module local
* variables.
*/
void import_invoke(jam_frame *frame,jam_list *res)
{
	const list_of_lists &args=frame->args_;
	assert(args.size()==5);
	jam_module_ptr source=args[0].empty()?frame->context_.get_root_module():
		frame->context_.bind_module(args[0].front());
	jam_module_ptr target=args[2].empty()?frame->context_.get_root_module():
		frame->context_.bind_module(args[0].front());
	bool do_localization=!is_empty_list(args[4]);

	jam_list::const_iterator s=args[1].begin(),send=args[1].end(),
		t=args[3].begin(),tend=args[3].end();
	for(;s!=send && t!=tend;s++,t++)
	{
		jam_module::rule_ent rule;
		if (!source->find_rule(*s,&rule))
			unknown_rule(frame,"IMPORT",source->get_name(),*s);

		target->import_rule(rule.rule_,*t,do_localization);
	}

	if (s!=send || t!=tend)
	{
		frame->context_.backtrace_line(frame->prev_);
		std::ostream &err=frame->context_.err();
		err<<"import error: length of source and target rule name lists don't match!"<<std::endl;
		err<<"    source: "<<args[1]<<std::endl;
		err<<"    target: "<<args[3]<<std::endl;
		frame->context_.backtrace(frame->prev_);
	}
}
native_rule_entry import_reg(JAM_ROOT_MODULE_NAME,"IMPORT",&import_invoke,
	jam_rule::sublist,"SOURCE_MODULE",
	jam_rule::vararg,"SOURCE_RULES",
	jam_rule::sublist,"TARGET_MODULE",
	jam_rule::vararg,"TARGET_RULES",
	jam_rule::sublist,"LOCALIZE");

///////////////////////////////////////////////////////////////////////////////////////
////// builtin_export() - EXPORT ( MODULE ? : RULES * )
///////////////////////////////////////////////////////////////////////////////////////
/*
* The EXPORT rule marks RULES from the SOURCE_MODULE as non-local
* (and thus exportable). If an element of RULES does not name a rule
* in MODULE, an error is issued.
*/
void export_invoke(jam_frame *frame,jam_list *res)
{
	const list_of_lists &args=frame->args_;
	assert(args.size()==2);
	jam_module_ptr source=args[0].empty()?frame->context_.get_root_module():
		frame->context_.bind_module(args[0].front());

	const jam_list &rules=args[1];
	for(jam_list::const_iterator f=rules.begin(),fend=rules.end();f!=fend;++f)
	{		
		if (!source->export_rule(*f))
			unknown_rule(frame,"EXPORT",source->get_name(),*f);
	}
}
native_rule_entry export_reg(JAM_ROOT_MODULE_NAME,"EXPORT",&export_invoke,
	jam_rule::sublist,"MODULE", 
	jam_rule::vararg, "RULES");

///////////////////////////////////////////////////////////////////////////////////////
////// builtin_delete_module() - DELETE_MODULE ( MODULE ? )
////// Clears all rules and variables from the given module.
///////////////////////////////////////////////////////////////////////////////////////
void delete_module_invoke(jam_frame *frame,jam_list *res)
{
	const list_of_lists &args=frame->args_;
	jam_module_ptr dest=args.empty() || args[0].empty()?frame->context_.get_root_module():
		frame->context_.bind_module(args[0].front());
	//Note: this is differenct from BJam. We don't remove module completely
	//because it can be referenced from some rules, we just delete all module
	//variables and rules.
	dest->clear();
}
native_rule_entry delete_module_reg(JAM_ROOT_MODULE_NAME,"DELETE_MODULE",&delete_module_invoke,
	jam_rule::sublist,"MODULE");

///////////////////////////////////////////////////////////////////////////////////////
////// builtin_import_module() - IMPORT_MODULE ( MODULES + : TARGET ? )
///////////////////////////////////////////////////////////////////////////////////////
void import_module_invoke(jam_frame *frame,jam_list *res)
{
	const list_of_lists &args=frame->args_;
	assert(args.size()==2);	
	jam_module_ptr dest=args[1].empty()?frame->context_.get_root_module():
		frame->context_.bind_module(args[1].front());
	
	for(jam_list::const_iterator f=args[0].begin(),fend=args[0].end();f!=fend;++f)
		dest->import_module(frame->context_.bind_module(*f).get());
}
native_rule_entry import_module_reg(JAM_ROOT_MODULE_NAME,"IMPORT_MODULE",
	&import_module_invoke,
	jam_rule::one_or_more,"MODULES",
	jam_rule::sublist,"TARGET");


///////////////////////////////////////////////////////////////////////////////////////
////// Registration
///////////////////////////////////////////////////////////////////////////////////////
void register_root_module(jam_context *ctx)
{
	echo_reg.register_in_context(ctx);
	swallow_reg.register_in_context(ctx);
	import_reg.register_in_context(ctx);
	export_reg.register_in_context(ctx);
	delete_module_reg.register_in_context(ctx);
	import_module_reg.register_in_context(ctx);
}
